package com.xlinur.android.moviecatalog;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;

import com.xlinur.android.moviecatalog.MovieCatalog;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Xml;

public class MovieDatabase {
	private static final String XML_ROOT = "movie";
	private static final String DATABASE_CREATE = "create table "
			+ MovieCatalog.DATABASE_TABLE + " (" + MovieCatalog.Movie[0]
			+ " integer primary key autoincrement, " + MovieCatalog.Movie[1]
			+ " integer not null, " + MovieCatalog.Movie[2]
			+ " text not null, " + MovieCatalog.Movie[3] + " text, "
			+ MovieCatalog.Movie[4] + " text, " + MovieCatalog.Movie[5]
			+ " integer, " + MovieCatalog.Movie[6] + " text, "
			+ MovieCatalog.Movie[7] + " text, " + MovieCatalog.Movie[8]
			+ " text, " + MovieCatalog.Movie[9] + " text);";

	private final Context context;

	private DatabaseHelper DBHelper;
	private SQLiteDatabase db;

	public MovieDatabase(Context ctx) {
		this.context = ctx;
		DBHelper = new DatabaseHelper(context);
	}

	private static class DatabaseHelper extends SQLiteOpenHelper {
		DatabaseHelper(Context context) {
			super(context, MovieCatalog.DATABASE_NAME, null,
					MovieCatalog.DATABASE_VERSION);
		}

		@Override
		public void onCreate(SQLiteDatabase db) {
			try {
				db.execSQL(DATABASE_CREATE);
			} catch (SQLException e) {

			}	
		}

		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
			try {
				db.execSQL("DROP TABLE IF EXISTS " + MovieCatalog.DATABASE_TABLE);
			} catch (SQLException e) {

			}	
			onCreate(db);
		}
	}

	public void drop() {
		try {
			db.execSQL("DROP TABLE IF EXISTS " + MovieCatalog.DATABASE_TABLE);
		} catch (SQLException e) {

		}
	}

	public void create() {
		try {
			db.execSQL(DATABASE_CREATE);
		} catch (SQLException e) {

		}
	}

	public MovieDatabase open() throws SQLException {
		try {
			db = DBHelper.getWritableDatabase();
		} catch (SQLException e) {

		}
		return this;
	}

	public void close() {
		DBHelper.close();
	}

	public Cursor getMovies(String order, String filter) {
		return db.query(MovieCatalog.DATABASE_TABLE, new String[] {
				MovieCatalog.Movie[0], MovieCatalog.Movie[1],
				MovieCatalog.Movie[2], MovieCatalog.Movie[3],
				MovieCatalog.Movie[4], MovieCatalog.Movie[5],
				MovieCatalog.Movie[6], MovieCatalog.Movie[7],
				MovieCatalog.Movie[8] }, filter, null, null, null, order);
	}

	public ArrayList<String> getAllIds() {
		ArrayList<String> result = new ArrayList<String>();
		Cursor cur = db.query(MovieCatalog.DATABASE_TABLE,
				new String[] { MovieCatalog.Movie[0] }, null, null, null, null,
				MovieCatalog.Movie[0] + " ASC");
		cur.moveToFirst();
		while (!cur.isAfterLast()) {
			result.add(cur.getString(0));
			cur.moveToNext();
		}
		return result;
	}

	public Map<String, String> getMovie(String id) {
		Cursor cur = db.query(MovieCatalog.DATABASE_TABLE, MovieCatalog.Movie,
				"_id = ?", new String[] { id }, null, null, null, null);
		int count = cur.getColumnCount();

		Map<String, String> result = new HashMap<String, String>();
		cur.moveToFirst();
		for (int i = 0; i < count; i++) {
			result.put(MovieCatalog.Movie[i], cur.getString(i));
		}
		cur.close();
		return prepareDataFromDB(result);
	}
	
	public Map<String, String> getEmptyMovie() {
		Map<String, String> result = new HashMap<String, String>();
		int count = MovieCatalog.Movie.length;
		for (int i = 0; i < count; i++) {
			result.put(MovieCatalog.Movie[i], "");
		}
		return prepareDataFromDB(result);
	}
	
	private Map<String, String> prepareDataFromDB(Map<String, String> data){
		Map<String, String> result = data;
		String xmlStr = result.get("xml");
		if (xmlStr==null){
			xmlStr = "";
		}
		result.remove("xml");
		int count = MovieCatalog.MovieXML.length;
		for (int i=0; i<count; i++){
			result.put(MovieCatalog.MovieXML[i], "");
		}
		StringReader reader = new StringReader(xmlStr);
		String tagName = null;
		XmlPullParserFactory factory;
		try{
			factory = XmlPullParserFactory.newInstance();
			XmlPullParser parser = factory.newPullParser();
			parser.setInput(reader);
			int eventType = parser.getEventType();
			while (eventType != XmlPullParser.END_DOCUMENT) {
				if (eventType == XmlPullParser.START_TAG) {
					tagName = parser.getName();
				} else if (eventType == XmlPullParser.END_TAG) {
					tagName = null;
				} else if (eventType == XmlPullParser.TEXT) {
					if (tagName != null && result.containsKey(tagName)) {
						result.put(tagName, parser.getText());
					}
					tagName = null;
				}
				eventType = parser.next();
			}
		} catch (XmlPullParserException e) {
		} catch (IOException e) {
		}
		return result;
	}
	
	private Map<String, String> prepareDataToDB(Map<String, String> data){
		Map<String, String> result = data;
		XmlSerializer serializer = Xml.newSerializer();
		StringWriter writer = new StringWriter();
		Boolean flag = false;
		try {
			serializer.setOutput(writer);
			serializer.startTag("", XML_ROOT);
			int count = MovieCatalog.MovieXML.length;
			for (int i=0; i<count; i++){
				String key = MovieCatalog.MovieXML[i];
				String value = result.get(key);
				serializer.startTag("", key);
				if (value!=null){
					serializer.text(value);
				} else {
					serializer.text("");
				}
				serializer.endTag("", key);
				result.remove(key);
			}
			serializer.endTag("", XML_ROOT);
			serializer.endDocument();
			flag = true;
		} catch (IllegalArgumentException e) {
		} catch (IllegalStateException e) {
		} catch (IOException e) {
		}
		
		if (flag) {
			result.put("xml", writer.toString());
		} else {
			result.put("xml", "");
		}
		return result;
	}

	private ContentValues mapValues(Map<String, String> dataSrc) {
		Map<String, String> data = prepareDataToDB(dataSrc);
		ContentValues result = new ContentValues();
		@SuppressWarnings("rawtypes")
		Set set = data.entrySet();
		@SuppressWarnings("rawtypes")
		Iterator it = set.iterator();
		while(it.hasNext()){
			@SuppressWarnings("unchecked")
			Map.Entry<String, String> entry = (Entry<String, String>) it.next();
			String value = entry.getValue();
			String key = entry.getKey();
			if (key.equals("modified")){
				long col;
				try {
					col = Long.parseLong(value);
				} catch (NumberFormatException e) {
					col = getDefaultModified();
				}
				result.put(key, col);
			} else if (key.equals("filesize")){
				long col;
				try {
					col = Long.parseLong(value);
				} catch (NumberFormatException e) {
					col = getDefaultFilesize();
				}
				result.put(key, col);
			} else if (!key.equals("_id")){
				result.put(key, value);
			}
		}
		return result;
	}

	public long insertMovie(Map<String, String> dataSrc, String id) {
		HashMap<String, String> data = (HashMap<String, String>) dataSrc;
		@SuppressWarnings("unchecked")
		long result = updateMovie(id, (Map<String, String>) data.clone());
		if (result == 0) {
			result = insertMovie(data);
		}
		return result;
	}

	// Overload
	public long insertMovie(Map<String, String> data) {
		return db.insert(MovieCatalog.DATABASE_TABLE, null, mapValues(data));
	}

	public int updateMovie(String id, Map<String, String> data) {
		return db.update(MovieCatalog.DATABASE_TABLE, mapValues(data),
				MovieCatalog.Movie[0] + "=" + id, null);
	}

	public int deleteMovie(String id) {
		return db.delete(MovieCatalog.DATABASE_TABLE, MovieCatalog.Movie[0]
				+ "=" + id, null);
	}

	public String checkMovieData(String[] data) {
		String result = "";
		// db.update(table, values, whereClause, whereArgs);
		// db.insert(table, nullColumnHack, values)
		// TODO Сделать правила проверки на валидность записываемых данных
		return result;
	}

	public Long getDefaultFilesize() {
		return (long) 0;
	}

	public Long getDefaultModified() {
		return System.currentTimeMillis();
	}
}